`timescale 1ns / 1ps

module pwc2_reorder
#(
    parameter N_HI      = 4,
    parameter N_IO      = 2,
    parameter N_BK      = 16,
    parameter N_CH      = 512,
    parameter BIT       = 16,
    parameter RAM_TYPE  = "block",
    parameter RAM_LATENCY = 2
)
(
    input   clk,
    input   rst,
    
    input   i_vld,
    output  i_rdy,
    input   [N_IO*BIT-1 : 0]    i_data,
    
    output  o_vld,
    input   o_rdy,
    output  [N_IO*BIT-1 : 0]    o_data
);

localparam DEPTH = N_CH / N_IO * 2 * N_HI;
localparam WIDTH = N_IO * BIT;

localparam   A_WIDTH = $clog2(DEPTH);
localparam ALO_WIDTH = $clog2(N_HI);
localparam AHI_WIDTH = A_WIDTH - ALO_WIDTH;

localparam FOLD_I = N_BK / N_IO;
localparam FOLD_O = N_CH / N_IO;

wire    fi_vld  [N_HI-1: 0];
wire    fi_rdy  [N_HI-1: 0];
wire    rd_vld  [N_HI-1: 0];
wire    rd_rdy  [N_HI-1: 0];
wire    wt_vld  [N_HI-1: 0];

wire                  mw_vld;
wire [A_WIDTH-1: 0]   mw_addr;
wire                  mr_rdy;
wire                  mr_vld;
wire [A_WIDTH-1: 0]   mr_addr;

wire [ALO_WIDTH-1: 0] i_sel;
wire [ALO_WIDTH-1: 0] o_sel;

wire [AHI_WIDTH-1: 0] w_addr_hi [N_HI-1: 0];
wire [AHI_WIDTH-1: 0] r_addr_hi [N_HI-1: 0];

wire i_ena;
wire i_last;
wire o_ena;
wire o_last;

assign  i_rdy = fi_rdy [i_sel];
assign mw_vld = wt_vld [i_sel];
assign mr_vld = rd_vld [o_sel];

assign mw_addr = { w_addr_hi[i_sel], i_sel };
assign mr_addr = { r_addr_hi[o_sel], o_sel };

assign i_ena =  i_rdy &  i_vld;
assign o_ena = mr_rdy & mr_vld;

zq_counter #(
    .N  (N_HI)
) inst_cnt_si
(
    .clk    (clk),
    .rst    (rst),
    .clken  (i_ena & i_last),
    .last   (),
    .out    (i_sel)
);
zq_counter #(
    .N  (N_HI)
) inst_cnt_so
(
    .clk    (clk),
    .rst    (rst),
    .clken  (o_ena & o_last),
    .last   (),
    .out    (o_sel)
);

zq_counter #(
    .N  (FOLD_I)
) inst_cnt_fi
(
    .clk    (clk),
    .rst    (rst),
    .clken  (i_ena),
    .last   (i_last),
    .out    ()
);
zq_counter #(
    .N  (FOLD_O)
) inst_cnt_fo
(
    .clk    (clk),
    .rst    (rst),
    .clken  (o_ena),
    .last   (o_last),
    .out    ()
);

genvar i;
generate
    for (i = 0; i < N_HI; i = i + 1)
    begin
        assign fi_vld[i] = (i_sel == i) ?  i_vld : 1'b0;
        assign rd_rdy[i] = (o_sel == i) ? mr_rdy : 1'b0;

        zq_fifo_ctrl #(
            .DEPTH ( DEPTH / N_HI )
        )
        inst_fifo_ctrl (
            .clk                     ( clk          ),
            .rst                     ( rst          ),

            .in_rdy                  ( fi_rdy[i]    ),
            .in_vld                  ( fi_vld[i]    ),

            .write_vld               ( wt_vld[i]    ),
            .write_addr              ( w_addr_hi[i] ),

            .read_rdy                ( rd_rdy[i]    ),
            .read_vld                ( rd_vld[i]    ),
            .read_addr               ( r_addr_hi[i] )
        );
    end
endgenerate

zq_sdpram_hs #(
    .ADDR_WIDTH (    A_WIDTH  ),
    .DATA_WIDTH (      WIDTH  ),
    .DEPTH      (      DEPTH  ),
    .LATENCY    ( RAM_LATENCY ),
    .RAMTYPE    ( RAM_TYPE    )
)
inst_sdpram_hs (
    .clk                     ( clk         ),
    .rst                     ( rst         ),

    .write_vld               ( mw_vld      ),
    .addr_w                  ( mw_addr     ),
    .din                     (  i_data     ),

    .read_rdy                ( mr_rdy      ),
    .read_vld                ( mr_vld      ),
    .addr_r                  ( mr_addr     ),

    .out_rdy                 (  o_rdy      ),
    .out_vld                 (  o_vld      ),
    .dout                    (  o_data     )
);

endmodule
